/***********************************************************************************************
 * File Name   :	voled_api.c
 * Programmer  : 	kmist
 * Start Date  : 	12/20/2020
 * Last Update : 	12-20, 2020
 *----------------------------------------------------------------------------------------------
 *Functions:
 *	static void oled_draw_point(uint8_t x, uint8_t y, uint8_t t);
 *	static void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
 *	static void oled_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
 *	static void oled_show_char(uint8_t x, uint8_t y, uint8_t ch, uint8_t size, uint8_t mode);
 *	static void oled_show_number(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode);
 *	static void oled_show_string(uint8_t x, uint8_t y, const uint8_t *p, size_t size, uint8_t mode);
 *	static void oled_draw_bmp(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp);
 *	static void oled_draw_bmp_point(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t mode, void *bmp);
 *	static void oled_draw_bmp_reversal(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp);
 *	static void oled_clean_gram(uint8_t data);
 *	static void oled_all_refresh_gram(uint8_t mode);
 ***********************************************************************************************/

#include <stdio.h>
#include "voled_config.h"
#include "voled_api.h"
#include "frontslib.h"

typedef unsigned char uchar;

uchar OLED_GRAMS[SCREAM_SIZE_W][SCREAM_ARRAY_H] = {0};
uchar vscreem[SCREAM_SIZE_W][SCREAM_SIZE_H] = {0};

static void oled_draw_point(uint8_t x, uint8_t y, uint8_t t);
static void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
static void oled_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode);
static void oled_show_char(uint8_t x, uint8_t y, uint8_t ch, uint8_t size, uint8_t mode);
static void oled_show_number(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode);
static void oled_show_string(uint8_t x, uint8_t y, const uint8_t *p, size_t size, uint8_t mode);
static void oled_draw_bmp(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp);
static void oled_draw_bmp_point(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t mode, void *bmp);
static void oled_draw_bmp_reversal(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp);
static void oled_clean_gram(uint8_t data);
static void oled_all_refresh_gram(uint8_t mode);

static void print_line(uint8_t len)
{
    uint16_t idx = 0;
    uint16_t line_len = (len+2)*2;
    
    for(idx = 0; idx< line_len; idx++)
    {
        printf("%s",WINDOWS_LINE_ROW);
    }
    
    printf("\n");
}
static void display_scream(uchar (*screem)[SCREAM_SIZE_H])
{
    int i = 0,j = 0;

    print_line(SCREAM_SIZE_W);
    for(i = 0; i < SCREAM_SIZE_H; i++)
    {
        printf("%s ",WINDOWS_LINE_COL);
        for(j = 0; j < SCREAM_SIZE_W; j++)
        {
            if(screem[j][i])
            {
                printf("%s", SCREAM_VALUE);
            }else{
                printf("%s",SCREAM_SPACE);
            }
        }
        printf(" %s\n", WINDOWS_LINE_COL);
    }
    print_line(SCREAM_SIZE_W);
}

static uint32_t oled_pow(uint8_t m, uint8_t n)
{
    uint32_t result = 1;
    while (n--)
        result *= m;
    return result;
}

Oled_api oled = {
    .draw_point         = oled_draw_point,
    .draw_line          = oled_draw_line,
    .draw_rect          = oled_draw_rect,
    .show_char          = oled_show_char,
    .show_number        = oled_show_number,
    .show_string        = oled_show_string,
    .draw_bmp           = oled_draw_bmp,
    .draw_bmp_point     = oled_draw_bmp_point,
    .draw_bmp_reversal  = oled_draw_bmp_reversal,
    .clean_gram         = oled_clean_gram,
    .refresh_all        = oled_all_refresh_gram,
};

/***********************************************************************
 *  函数名: oled_draw_point
 *  描述：在缓冲区打印一个像素
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(1-64)
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_draw_point(uint8_t x, uint8_t y, uint8_t t)
{
    uint8_t bx, temp = 0;
    int pos = 0;


    if (x > (X_WIDTH - 1) || y > (Y_WIDTH - 1))
        return;
    pos = y / 8; //8是bit位
    bx = y % 8;
    temp = 1 << (7 - bx);

    if (t)
        OLED_GRAMS[x][pos] |= temp;
    else
        OLED_GRAMS[x][pos] &= ~temp;
}


/***********************************************************************
 *  函数名: oled_draw_line
 *  描述：画一条直线
 *  参数：uint8_t x1、y1: （x1,y1)点1
 *  参数：uint8_t x2、y2: (x2,y2)点2
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode)
{
    float x = 0.0f, y = 0.0f;
    int8_t minx = x1, miny = y1; //p1
    int8_t maxx = x2, maxy = y2; //p2
    if (x1 == x2)
    {
        if (y1 > y2)
        {
            minx = x2, miny = y2;
            maxx = x1, maxy = y1;
        }
    }
    else if (x1 > x2)
    {
        minx = x2, miny = y2;
        maxx = x1, maxy = y1;
    }

    if (minx == maxx)
    {
        for (y = miny; y <= maxy; y++)
        {
            oled_draw_point(minx, y, mode);
        }
    }
    else
    {
        for (x = minx; x <= maxx; x++)
        {
            y = (maxy - miny) * (x - maxx) / (maxx - minx) + maxy;
            oled_draw_point(x, y, mode);
        }
    }
}

/***********************************************************************
 *  函数名: oled_drow_rect
 *  描述：画一条矩形
 *  参数：uint8_t x1、y1: （x1,y1)点1
 *  参数：uint8_t x2、y2: (x2,y2)点2
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t mode)
{
    int8_t minx = x1, miny = y1;
    int8_t maxx = x2, maxy = y2;
    if (x1 > x2)
    {
        minx = x2, miny = y2;
        maxx = x1, maxy = y1;
    }

    oled_draw_line(minx, miny, maxx, miny, mode);
    oled_draw_line(maxx, miny, maxx, maxy, mode);
    oled_draw_line(minx, miny, minx, maxy, mode);
    oled_draw_line(minx, maxy, maxx, maxy, mode);
}

/***********************************************************************
 *  函数名: oled_show_char
 *  描述：在缓冲区打印一个字符
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(1-64)
 *  参数: uint8_t ch 要打印的字符
 *  参数: uint8_t size 字体大小
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_show_char(uint8_t x, uint8_t y, uint8_t ch, uint8_t size, uint8_t mode)
{
    uint8_t temp, t, t1;
    uint8_t y0 = y;
    uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2);

    if (size == FONTSIZE_32_16)
    {
        //ch = (ch-48)*2;
        ch = (ch - ' ') * 2; /* 因为32大小的字体每个占用2个数组元素，所以每次步进2 */
    }
    else
    {
        ch = ch - ' ';
    }

    for (t = 0; t < csize; t++)
    {
        if (size == FONTSIZE_12_6)
            temp = oled_asc2_1206[ch][t]; //调用1206字体
        else if (size == FONTSIZE_16_8)
            temp = oled_asc2_1608[ch][t]; //调用1608字体
        else if (size == FONTSIZE_32_16)
            temp = oled_asc2_3216[ch][t];
        else
            return;

        for (t1 = 0; t1 < 8; t1++)
        {
            if (temp & 0x80)
                oled_draw_point(x, y, mode);
            else
                oled_draw_point(x, y, !mode);

            temp <<= 1;
            y++;
            if ((y - y0) == size)
            {
                y = y0;
                x++;
                break;
            }
        }
    }
}


/***********************************************************************
 *  函数名: oled_show_number
 *  描述：在缓冲区打印数字
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(1-64)
 *  参数: uint32_t num 要显示的数字
 *  参数: uint8_t  len 数字的长度 
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_show_number(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size, uint8_t mode)
{
    uint8_t t, temp;
    uint8_t enshow = 0;
    for (t = 0; t < len; t++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;
        if (enshow == 0 && t < (len - 1))
        {
            if (temp == 0)
            {
                oled_show_char(x + (size / 2) * t, y, '0', size, mode);
                continue;
            }
            else
                enshow = 1;
        }
        oled_show_char(x + (size / 2) * t, y, temp + '0', size, mode);
    }
}


/***********************************************************************
 *  函数名: oled_show_string
 *  描述：在缓冲区打印字符串
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(1-64)
 *  参数: const uint8_t *p 要显示的字符串指针
 *  参数: size_t size 字体大小 
 *  参数：uint8_t mode 显示模式(1:黑底白字 0：白底黑字)
 *  返回值： void
 ***********************************************************************/
static void oled_show_string(uint8_t x, uint8_t y, const uint8_t *p, size_t size, uint8_t mode)
{
    while (*p != '\0')
    {
        if (x > MAX_CHAR_POSX)
        {
            x = 0;
            y += size;
        }
        if (y > MAX_CHAR_POSY)
        {
            y = x = 0;
            //oled_clear(i2c_num);
        }

        oled_show_char(x, y, *p, size, mode);
        x += (size / 2);
        p++;
    }
}
/***********************************************************************
 *  函数名: oled_draw_bmp
 *  描述：以page为单位在缓冲区绘画一张bmp图片，速度快，但是不灵活(0-8) 融合的形式
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(0-1)
 *  参数: uint8_t w bmp宽度(竖着总量/8)
 *  参数: uint8_t l bmp长度
 *  参数: uint8_t model 模式： 0 混合(融合)模式； 1：替换(覆盖))模式;
 *  参数：void *bmp bmp资源
 ***********************************************************************/
static void oled_draw_bmp(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp)
{
    uint8_t m, n = 0;
    uint8_t(*tmp)[w] = (uint8_t(*)[w])bmp;
    for (n = 0; n < w; n++)
    {
        for (m = 0; m < l; m++)
        {
            if (model)
                OLED_GRAMS[m + x][n + y] = tmp[m][n]; //32*2 //16*2
            else
                OLED_GRAMS[m + x][n + y] |= tmp[m][n];
        }
    }
}

/***********************************************************************
 *  函数名: oled_draw_bmp_point
 *  描述：在像素为单位缓冲区绘画一张bmp图片，速度慢，但是灵活(0-64)
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标(0-64)
 *  参数: uint8_t w bmp宽度(竖着总量/8)
 *  参数: uint8_t l bmp长度
 *  参数：void *bmp bmp资源
 *  参数：uint8_t mode 是否反色（1：白底黑图 0：黑底白图Y）
 ***********************************************************************/
static void oled_draw_bmp_point(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t mode, void *bmp)
{
    uint8_t m, n = 0, t1 = 0;
    uint8_t temp = 0;
    uint8_t y0 = y;
    uint8_t x0 = x;
    uint8_t(*tmp)[w] = (uint8_t(*)[w])bmp;

    for (n = 0; n < w; n++)
    {
        for (m = 0; m < l; m++)
        {
            temp = tmp[m][n];
            for (t1 = 0; t1 < 8; t1++)
            {
                if (temp & 0x80)
                    oled_draw_point(x, y, mode);
                else
                    oled_draw_point(x, y, !mode);

                temp <<= 1;
                y++;
            }
            x++;
            y = y0;
        }
        x = x0;
        y += 8;
        y0 += 8;
    }
}

/***********************************************************************
 *  函数名: oled_draw_bmp_reversal
 *  描述：在缓冲区 垂直反转 绘画一张bmp图片
 *  参数：uint8_t x 横坐标
 *  参数：uint8_t y 纵坐标
 *  参数: uint8_t w bmp宽度(竖着总量/8)
 *  参数: uint8_t l bmp长度
 *  参数: uint8_t model 模式： 0 混合(融合)模式； 1：替换(覆盖))模式;
 *  参数：void *bmp bmp资源
 ***********************************************************************/
static void oled_draw_bmp_reversal(uint8_t x, uint8_t y, uint8_t w, uint8_t l, uint8_t model, void *bmp)
{
    uint8_t m, n = 0;
    uint8_t(*tmp)[w] = (uint8_t(*)[w])bmp;
    for (n = 0; n < w; n++)
    {
        for (m = 0; m < l; m++)
        {
            if (model)
                OLED_GRAMS[l + x - m - 1][n + y] = tmp[m][n];
            else
                OLED_GRAMS[l + x - m - 1][n + y] |= tmp[m][n];
        }
    }
}

/***********************************************************************
 *  函数名: oled_clean_gram
 *  描述：将缓冲区写满某个数值，一般用于清屏!!!
 *  参数：uint8_t data  要填充的值
 *  返回值： void
 ***********************************************************************/
static void oled_clean_gram(uint8_t data)
{
    uint8_t i, n;
    for (i = 0; i < SCREAM_ARRAY_H; i++)
    {
        for (n = 0; n < SCREAM_SIZE_W; n++)
        {
            OLED_GRAMS[n][i] = data;
        }
    }
}

/***********************************************************************
 *  函数名: oled_all_refresh_gram
 *  描述：将缓存刷新到屏幕上
 *  参数：uint8_t mode
 *      @ 0: 刷新完保留缓存
 *      @ 1: 刷新完清空缓存
 *  返回值： void
 ***********************************************************************/
static void oled_all_refresh_gram(uint8_t mode)
{
    int i = 0;
    int j = 0;
    int k = 0; //8 bit位

    //ba 16*2 16*16  == 32*2 = 32*16
    for (i = 0; i < SCREAM_SIZE_W; i++)
    {
        for (j = 0; j < SCREAM_ARRAY_H; j++)
        {
            for (k = 0; k < 8; k++)
            {
                vscreem[i][j * 8 + k] = 0x01 & (OLED_GRAMS[i][j] >> (7 - k));
            }
            if(mode)
            {
                OLED_GRAMS[i][j] = 0;
            }
        }
    }
    display_scream(vscreem);
}
























